/*******************************************************************************
 * Copyright 2011 Alexandre Zglav and Morphiastic
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package com.morphiastic;

import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;

import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Date;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
import org.bson.types.ObjectId;
import org.elasticsearch.common.xcontent.XContentBuilder;
import com.morphiastic.analysis.analyzers.ElasticAnalyzer;
import com.morphiastic.analysis.analyzers.ElasticAnalyzerFactory;
import com.morphiastic.annotations.*;
import com.morphiastic.conversion.ElasticConverter;
import com.morphiastic.fieldbridge.VoidIndexedFieldBridge;
import com.morphiastic.utils.ReflectionTools;
import java.lang.reflect.AccessibleObject;


/**
 * Definition of how a standard ( primitive and primitive wrapper objects ) field should be indexed
 * @author Alex
 *
 */
public class StandardIndexedField implements IndexedField{

	public static final ElastiField defaultElastiFieldAnnotation = DefaultElastiFieldValueAccessor.get();
	
//	Field field;
//	Class realFieldType; //the actual type of the field
	private ElastiField fieldAnnotation; //not really used ... but lets keep it we never know
	private Class<?> fieldType;
	private ElastiFieldType elastiFieldType; //the Elastic search type of the field : this defaults to ElastiFieldType.auto which means we need to figure out the core type by using the entity's field type ( String Integer etc ... )  
	private Type fieldGenericType;
	private String fieldOriginalName;
	private String propertyName;
	private Store store;
	private Analyze analyze; //used only for strings 
	private Index index; //index property of the annotation ... ... Used only for NON string types
	private TermVector temVector;
	private float boost;
	private ElasticAnalyzer analyzer;
	private ElasticAnalyzer searchAnalyzer;
	
	private String indexNullAs;
	boolean omitNorms = false;
	boolean omitTermFreqPos = false;
	private Analyzer analyzerAnnotation;
	
	private boolean methodMode = false; //is the field actually a method ? 
	
	//optional fieldBridge annotations if we need to index the field differently than from the standard way
	private ElastiFieldBridge fieldBridgeAnnotation;
	private IndexedFieldBridge fieldBridge;
	private boolean fieldBridgeBypassEnabled = false;
	
	ElasticStore es;
	
	
	public StandardIndexedField(Field field, ElasticStore es) {
		this(field.getAnnotation(ElastiField.class), field.getType(), field.getGenericType(), field.getName(), es);
	}
 
	
	public StandardIndexedField(Method method, ElasticStore es){
		this(method.getAnnotation(ElastiField.class), method.getReturnType(), method.getGenericReturnType(), method.getName(), es);
		this.methodMode = true;
	}
	
	
	//Manual constructor used only for Map special case
	protected StandardIndexedField(ElastiField fan, Class<?> fieldType, Type fieldGenericType, String fieldName, ElasticStore es ){
		
		if(fan==null)throw new IllegalArgumentException("The field was not annotated with the propper ElastiField annotation");
		this.fieldAnnotation = fan;
		this.es = es;
		
		boolean isMap = Map.class.isAssignableFrom(fieldType);
		if(isMap)throw new IllegalArgumentException("The field is a Map and was not annotated with the propper ElastiMap annotation"); 
		
		this.fieldType = fieldType;
		this.elastiFieldType = fan.elastiFieldType();
		this.fieldGenericType = fieldGenericType;
		this.fieldOriginalName = fieldName;
		this.propertyName = fan.name();
		if(this.propertyName.isEmpty())this.propertyName = fieldName;
		this.store = fan.store();
		this.analyze = fan.analyze();
		this.index = fan.index();
		this.temVector = fan.termVector();
		this.analyzer = ElasticAnalyzerFactory.getAnalyzerFromAnnotation(fan.analyzer());
		this.analyzerAnnotation = fan.analyzer();
		this.searchAnalyzer = ElasticAnalyzerFactory.getAnalyzerFromAnnotation(fan.searchAnalyzer());
		this.boost = fan.boost().value();
		
//		fan.fieldConverter(), TODO change this to the propper fieldConverter
		this.fieldBridgeAnnotation = fan.fieldConverter();
		if(!this.fieldBridgeAnnotation.bridgeClass().equals(VoidIndexedFieldBridge.class)){
			this.fieldBridgeBypassEnabled = fieldBridgeAnnotation.fullyOverrideDefault();
//			if(es.getKnownFieldBridges().get(this.fieldBridgeAnnotation.bridgeClass()) != null){
			if(es.getKnownFieldBridge(this.fieldBridgeAnnotation) != null){	
				//fieldbridge already instantiated
				System.out.println("fieldbridge already instantiated  WITH FIELD NAME "+ this.fieldOriginalName + " AND ANNOTATION NAME : " + this.fieldBridgeAnnotation.name());
				this.fieldBridge = es.getKnownFieldBridge(this.fieldBridgeAnnotation);
			}else{
				//instantiate the fieldbridge if needed
				try {
					System.out.println("INSTANTIATING NEW FIELDBRIDGE WITH FIELD NAME "+ this.fieldOriginalName + " AND ANNOTATION NAME : " + this.fieldBridgeAnnotation.name());
					IndexedFieldBridge newFB = this.fieldBridgeAnnotation.bridgeClass().newInstance();
					this.fieldBridge = newFB;
					HashMap<String,String> fbridgeParams = ElasticStore.paramsAnnotationArrayToHashMap(this.fieldBridgeAnnotation.params());
					this.fieldBridge._init(this.fieldBridgeAnnotation.name(), this.fieldBridgeAnnotation.bridgeClass(), this.fieldBridgeAnnotation.fullyOverrideDefault(), fbridgeParams);
					this.fieldBridge.init(es, fbridgeParams);
					es.addKnownFieldBridge(this.fieldBridgeAnnotation, this.fieldBridge);
//					es.getKnownFieldBridges().put(this.fieldBridgeAnnotation.bridgeClass(), newFB);
				} catch (InstantiationException e) {
					// TODO Log this properly
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					// TODO Log this properly
					e.printStackTrace();
				}
			}
			
		}
		
		
		this.indexNullAs =  fan.indexNullAs();
		this.omitNorms = fan.omitNorms();
		this.omitTermFreqPos = fan.omitTermFreqAndPos();
	}
	
	
	 public String getFieldOriginalName(){
		 return this.fieldOriginalName;
	 }



	/**
	 *  Method used to set the field parameters in the ES index for the given field
	 * @param mappingBuilder
	 * @throws IOException 
	 */
	public void addIndexFieldSettings(XContentBuilder mappingBuilder) throws IOException {
//		if(isMap){ 
//			mappingBuilder.startObject(this.fieldOriginalName);
//				mappingBuilder.field("index_name", this.propertyName);
//				mappingBuilder.field("type", getElasticTypeNameOfField(this.field));//should be object
//				mappingBuilder.startObject("properties");//props of a map a key and value
//					mappingBuilder.startObject("key"); //
//						mappingBuilder.field("type", getElasticTypeNameOfField(this.field));
//						mappingBuilder.field("store", this.store.toString());
//						mappingBuilder.field("index", this.analyze.toString());		
//					
//					
//					mappingBuilder.startObject("value");
//					
//		}else{
		
		if(this.fieldBridge == null  || !this.fieldBridgeBypassEnabled){
			//the fieldbridge doesnt want us to bypass the regular mapping : do the regular thing first
			mappingBuilder.startObject(this.fieldOriginalName); //
//			mappingBuilder.field("index_name", this.propertyName); // commented out on 09.06.2012 see next line to know why
			if(!this.fieldOriginalName.equals(this.propertyName))mappingBuilder.field("index_name", this.propertyName);
			
			String typeStr = null;
			if(this.elastiFieldType == ElastiFieldType.auto){
				 typeStr = getElasticTypeName(this.fieldType, this.fieldGenericType);
				if(!typeStr.equals("object")){
					mappingBuilder.field("type", typeStr );
				}
			}else{
				
				
				 typeStr =  this.elastiFieldType.toString();
				if(!typeStr.equals("object")){
					mappingBuilder.field("type", typeStr );
				}
				
//				mappingBuilder.field("type", this.elastiFieldType.toString());
			}
			
			if(this.store != null && !this.store.equals(defaultElastiFieldAnnotation.store()))mappingBuilder.field("store", this.store.toString());
			
			if(!this.temVector.equals(defaultElastiFieldAnnotation.termVector()))mappingBuilder.field("term_vector", this.temVector.toString());
			if(this.boost != defaultElastiFieldAnnotation.boost().value())mappingBuilder.field("boost", this.boost);
			if(!this.indexNullAs.equals(ElastiField.DO_NOT_INDEX_NULL) ){
				mappingBuilder.field("null_value", this.indexNullAs);
			}
			
	 
			System.out.println("fieldOriginalName " + fieldOriginalName);
			
			//tricky combination as analyze and index are different depending on 
			//the core type of the field Analyze is used for text fields (String) and Index is used for other types 
			System.out.println(typeStr);
			if(typeStr.equals("string")){
				if(this.analyze.equals(Analyze.analyzed)){
					
					//no need to add the "index" field to the mapping because Analyze.analyzed is the default BUT we want to add the name of the analyzer
					mappingBuilder.field("analyzer", this.analyzer.getDefName());
				}else{
					mappingBuilder.field("index", this.analyze.toString());	
				}
			}else{
				try {
					System.out.println(this.index);
					//we are not dealing with a string so we should fall back to the value of Index instead and add it ONLY if its not default
					if(!this.index.equals(defaultElastiFieldAnnotation.index()))mappingBuilder.field("index", this.index.toString());	
				} catch (Exception e) {
					
					e.printStackTrace();
					
				}
				
			}
			
			 
			
			
		 
			
			mappingBuilder.endObject();
			
			//TODO check this
//			if(this.searchAnalyzer == null){
//				//use this.analyzer for both index and search
//				boolean customized = this.analyzer.hasCustomParams();
//				if(!customized){
//					//we should redefine it at the index level
//				}
//			}
		}
		
		if(this.fieldBridge != null){
			//forward to the fieldBridge so that it can do its mapping ob
			this.fieldBridge.addIndexFieldSettings(mappingBuilder);
		}
		 
	
	}

	
	
	 
	
	private boolean isNumericType(){
		String typeStr =  this.elastiFieldType.toString();
		 
		
		return typeStr.equals("float") ||
				typeStr.equals("double") ||
				typeStr.equals("byte") ||
				typeStr.equals("short") ||
				typeStr.equals("integer") ||
				typeStr.equals("long") ;
		
	}
	
	
	 
	@Deprecated
	public void addFieldSettingsMap(HashMap<String, Object> props){

		if(this.fieldBridge == null  || !this.fieldBridgeBypassEnabled){
			
			//build the standard/default mapping as a map
			Map<String,Object> m = new HashMap<String, Object>();
			if(this.elastiFieldType == ElastiFieldType.auto){
				m.put("type", getElasticTypeName(this.fieldType, this.fieldGenericType));
			}else{
				m.put("type", this.elastiFieldType.toString());
			}
//			if(!this.fieldOriginalName.equals(this.propertyName))m.put("index_name", this.propertyName);
			if(!this.fieldOriginalName.equals(this.propertyName))m.put("index_name", this.propertyName);
			if(this.store != null && !this.store.equals(defaultElastiFieldAnnotation.store()))m.put("store", this.store.toString());
			if(!this.analyze.equals(defaultElastiFieldAnnotation.analyze()))m.put("index", this.analyze.toString());		
			if(!this.temVector.equals(defaultElastiFieldAnnotation.termVector()))m.put("term_vector", this.temVector.toString());
			if(this.boost != defaultElastiFieldAnnotation.boost().value())m.put("boost", Double.valueOf(""+this.boost)); //ugly ... yuckyuck
			
			if(!this.indexNullAs.equals(ElastiField.DO_NOT_INDEX_NULL) ){
				m.put("null_value", this.indexNullAs.toString());
			}
			
			if(this.analyze.equals(Analyze.analyzed)){
				m.put("analyzer", this.analyzer.getDefName());
			}
			
			
			//TODO add query time analyzer here aswell
			
			props.put(this.fieldOriginalName, m);
		}
		
		if(this.fieldBridge!=null){
			this.fieldBridge.addFieldSettingsMap(props);
		}
		
		
	}
	
	
	
	@Override
	public XContentBuilder writeIndexedField(XContentBuilder mappingBuilder ,  Object o, ElasticStore es, Integer maxCircularRefDepth, IdentityHashMap<AccessibleObject, Integer> known) throws IOException {
		if(o==null){
			String nullStr = null;
			mappingBuilder.field(getPropertyName(), nullStr );
			return mappingBuilder;
		}
		try {
//			System.out.println("StandardIndexedField writeindexedField o.getClass = " + o.getClass().getName() + " and fieldoriginalName = " + fieldOriginalName);

			
			AccessibleObject field = null;
			
			if(!methodMode){
				Field fieldF = ReflectionTools.getDeclaredFieldFromHierarchy(o.getClass(), fieldOriginalName);
				field = fieldF;
			}
			field.setAccessible(true);
			
			//check that we are not going too deeply in the circular reference
			Integer curFieldDepth = known.get(field);
			if(curFieldDepth == null)curFieldDepth=0;
			if(maxCircularRefDepth!=null && curFieldDepth>maxCircularRefDepth){
				return mappingBuilder;
			}
			curFieldDepth++;
			known.put(field, curFieldDepth);
			
			
			
			Class<?> fieldType = null;
			Object value = null; //field.get(o);
			//This is dirty but i cant find a way around it as Map.Entry looses the information when dealing with embedded collection ... 
			if(o instanceof Map.Entry){
//				System.out.println("o instanceof Map.Entry ...");
				Map.Entry mapentry = (Map.Entry)o;
				if(fieldOriginalName.equals("key")){
					value = mapentry.getKey();
					fieldType = mapentry.getKey().getClass();
				}else if(fieldOriginalName.equals("value")){
					value = ((Map.Entry)o).getValue();
					fieldType = mapentry.getValue().getClass();
				}
			}else{
				if(!methodMode){ //direct field get
					value = ((Field) field).get(o);
					fieldType = ((Field) field).getType();
//					System.out.println("foundValue = " + value + " and fieldType = " + fieldType.getName().toString());
				}else{
					value = ((Method) field).invoke(o);
					fieldType = ((Method) field).getReturnType();
				}
			}
			 
			
			if(this.fieldBridge == null || !this.fieldBridgeBypassEnabled){
				ElasticConverter<?> converter = es.getTypeConverter(fieldType);
				System.out.println("converter = " + converter);
				
				
				if(fieldType.isArray() || Collection.class.isAssignableFrom(fieldType)){
					if(fieldType.isArray()){
							Object[] arr = null;
							if (value instanceof Object[]){
								arr = (Object[]) value;
							}else{
								//primitives here ... 
								
								 int arrlength = Array.getLength(value);
								 Object[] outputArray = new Object[arrlength];
							     for(int i = 0; i < arrlength; ++i){
							       outputArray[i] = Array.get(value, i); //this will autobox .. :)
							     }
							     arr = outputArray;
							}
							mappingBuilder.startArray(getPropertyName());
							for (Object object : arr) {
								mappingBuilder.value(converter.toIndexValue(object));
							}
							mappingBuilder.endArray();
					}else{
						//its a collection of some sort
						Iterable iterable = (Iterable) value;
						mappingBuilder.startArray(getPropertyName());
						for (Object object : iterable) {	
							mappingBuilder.value(converter.toIndexValue(object));
						}
						mappingBuilder.endArray();
					}
				}else{
					//its neither a collection nor an array ... just a plain regular field
					mappingBuilder.field(getPropertyName(), converter.toIndexValue(value) );
				}
			}
			
			if(this.fieldBridge != null){
				//we have a fieldBridge so we want to write the field using the custom method
				if(fieldType.isArray() || Collection.class.isAssignableFrom(fieldType)){
					if(fieldType.isArray()){
						Object[] arr = (Object[]) value;
						mappingBuilder.startArray(getPropertyName());
						for (Object object : arr) {
							this.fieldBridge.writeIndexedField(mappingBuilder, getPropertyName(), object, es, maxCircularRefDepth, known);
						}
						mappingBuilder.endArray();
					}else{
						Iterable iterable = (Iterable) value;
						mappingBuilder.startArray(getPropertyName());
						for (Object object : iterable) {	
							this.fieldBridge.writeIndexedField(mappingBuilder, getPropertyName(), object, es, maxCircularRefDepth, known);
						}
						mappingBuilder.endArray();
					}
				}else{
					this.fieldBridge.writeIndexedField(mappingBuilder, getPropertyName(), value, es, maxCircularRefDepth, known);
				}
			}
		} catch (SecurityException e) { //swallowed shiat ... at least log this ffs.... 
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return mappingBuilder;
	}
	
	 
	
	
	@Override
	public Set<ElasticAnalyzer> getAnalyzers() {
		Set<ElasticAnalyzer> allAnalyzerDefs = new HashSet<ElasticAnalyzer>();
		allAnalyzerDefs.add(getAnalyzer());
		if(getSearchAnalyzer()!=null)allAnalyzerDefs.add(getSearchAnalyzer());
		return allAnalyzerDefs;
	}
	
	
	
	
	
	
	
	
	
	
	
	
	/**
	 * @return the propertyName
	 */
	public String getPropertyName() {
		return propertyName;
	}

	/**
	 * @param propertyName the propertyName to set
	 */
	public void setPropertyName(String propertyName) {
		this.propertyName = propertyName;
	}

	/**
	 * @return the store
	 */
	public Store getStore() {
		return store;
	}

	/**
	 * @param store the store to set
	 */
	public void setStore(Store store) {
		this.store = store;
	}

	/**
	 * @return the analyze
	 */
	public Analyze getAnalyze() {
		return analyze;
	}

	/**
	 * @param analyze the analyze to set
	 */
	public void setAnalyze(Analyze analyze) {
		this.analyze = analyze;
	}

	/**
	 * @return the temVector
	 */
	public TermVector getTemVector() {
		return temVector;
	}

	/**
	 * @param temVector the temVector to set
	 */
	public void setTemVector(TermVector temVector) {
		this.temVector = temVector;
	}

	 

	/**
	 * @return the boost
	 */
	public float getBoost() {
		return boost;
	}

	/**
	 * @param boost the boost to set
	 */
	public void setBoost(float boost) {
		this.boost = boost;
	}

	 

	/**
	 * @return the indexNullAs
	 */
	public String getIndexNullAs() {
		return indexNullAs;
	}

	/**
	 * @param indexNullAs the indexNullAs to set
	 */
	public void setIndexNullAs(String indexNullAs) {
		this.indexNullAs = indexNullAs;
	}










	/**
	 * @return the analyzer
	 */
	public ElasticAnalyzer getAnalyzer() {
		return analyzer;
	}










	/**
	 * @param analyzer the analyzer to set
	 */
	public void setAnalyzer(ElasticAnalyzer analyzer) {
		this.analyzer = analyzer;
	}






	public void prepareAnalyzers(ElasticStore elasticStore){
		
		
		//get all Analyzers annotations
		
		
	}













	public static String getElasticTypeNameOfField(Field field){
		Class<?> type = field.getType();
		return getElasticTypeName(type, field.getGenericType());
	}


	/**
	 * Returns the name of the Elastic raw type for the given class
	 *
	 * @param type
	 * @return
	 */
	public static String getElasticTypeName(Class<?> type, Type genericType){
		if(type.equals(String.class)){
			return "string";
		}else if(type.equals(Integer.class) || type.equals(Integer.TYPE)){
			return "integer";
		}else if(type.equals(Long.class) || type.equals(Long.TYPE)){
			return "long";
		}else if(type.equals(Float.class) || type.equals(Float.TYPE)){
			return "float";
		}else if(type.equals(Double.class) || type.equals(Double.TYPE)){
			return "double";
		}else if(type.equals(Byte.class) || type.equals(Byte.TYPE)){
			return "byte";
		}else if(type.equals(Short.class) || type.equals(Short.TYPE)){
			return "short";
		}else if(type.equals(Boolean.class) || type.equals(Boolean.TYPE)){
			return "boolean";
		}else if(Date.class.isAssignableFrom(type)){
			return "date";
		}else if(type.equals(ObjectId.class)){
				return "string";
		}else if(type.isArray()){
//			 caca get the type of the object of the array
			 Class<?> dataType = type.getComponentType();
			 return getElasticTypeName(dataType, genericType);
		}else if(Collection.class.isAssignableFrom(type)){
//			 Type gerType = field.getGenericType(); 
			 if (genericType instanceof ParameterizedType) {  
		            ParameterizedType pt = (ParameterizedType) genericType;  
		            Class  c = (Class) pt.getActualTypeArguments()[0];
		            return getElasticTypeName(c, genericType);
		      }  
			 throw new IllegalArgumentException("TYPE NOT SUPPORTED FOR STANDARD INDEXING, USE A FIELD BRIDGE OR AN EMBEDDED FIELD INSTEAD : " + type.getName());
//			ParameterizedType parType = (ParameterizedType) field.getGenericType();
//			System.out.println("collection parametrized type args = " + parType.getActualTypeArguments());
//			Type realType = parType.getActualTypeArguments()[0];
//			System.out.println("we have an collection : firing getElasticTypeName with realType class name = " + realType.getClass().getName() + " and filed = " + field.getName());
//			return getElasticTypeName(realType.getClass(), field);
		}else if(Map.class.isAssignableFrom(type)){
			return "object";
		}else{
			System.out.println("TYPE NOT SUPPORTED FOR STANDARD INDEXING, USE A FIELD BRIDGE OR AN EMBEDDED FIELD INSTEAD  : " + type.getName());
			throw new IllegalArgumentException("TYPE NOT SUPPORTED FOR STANDARD INDEXING, USE A FIELD BRIDGE OR AN EMBEDDED FIELD INSTEAD  : " + type.getName());
		}
	}



//	public Class<?> getGenericType(Class<?> clazz) {
//	    Class result = null;
//	    Type type = clazz.getGenericSuperclass();
//	    if (type instanceof ParameterizedType) {
//	        ParameterizedType pt = (ParameterizedType) type;
//	        Type[] fieldArgTypes = pt.getActualTypeArguments();
//	        result = (Class) fieldArgTypes[0];
//	    }
//	    return result;
//	}



	/**
	 * @return the searchAnalyzer
	 */
	public ElasticAnalyzer getSearchAnalyzer() {
		return searchAnalyzer;
	}











	/**
	 * @param searchAnalyzer the searchAnalyzer to set
	 */
	public void setSearchAnalyzer(ElasticAnalyzer searchAnalyzer) {
		this.searchAnalyzer = searchAnalyzer;
	}


 
	
	
	
	
	//hack
	static final class DefaultElastiFieldValueAccessor{
		
		@ElastiField
		public static final String field ="";
		
		static ElastiField get(){
			try {
				return DefaultElastiFieldValueAccessor.class.getField("field").getAnnotation(ElastiField.class) ;
			} catch (SecurityException e) {
				e.printStackTrace();
				return null;
			} catch (NoSuchFieldException e) {
				e.printStackTrace();
				return null;
			}
		}
	}








	/**
	 * @return the fieldBridgeAnnotation
	 */
	public ElastiFieldBridge getFieldBridgeAnnotation() {
		return fieldBridgeAnnotation;
	}


	/**
	 * @param fieldBridgeAnnotation the fieldBridgeAnnotation to set
	 */
	public void setFieldBridgeAnnotation(ElastiFieldBridge fieldBridgeAnnotation) {
		this.fieldBridgeAnnotation = fieldBridgeAnnotation;
	}


	
	
	
	
	
	
	
	
	
}
